package tw.right929.proxy
{
	import flash.events.*;
	import flash.utils.*;
	
	import tw.right929.event.AdvancedEventDispatcher;
	
	use namespace flash_proxy
	
	/**
	 * 這個物件是動態的，可以完全代理另一個物件。
	 * 主要功能如下：
	 * 1.代理另一個物件的所有屬性(無法區分唯讀、唯寫)和方法。
	 * 2.若被代理物件發出這個Proxy有偵聽的事件，一律使用Event.clone轉發。
	 * 3.使用$alias產生屬性及方法的別名。
	 * 注意事項：
	 * 1.for...in迴圈，只會跑這個Proxy上的屬性及方法(含別名)，不會有被代理者的。
	 * 2.若被代理者的屬性、方法名和Proxy擁有的同名，Proxy的屬性及方法優先。
	 * 3.動態加入的屬性方法，不會加到被代理者身上。
	 * 4.$super(被代理者)的更換，不會影響Proxy現有的屬性、方法。已加到Proxy上的事件偵聽式也不會消失。
	 */
	public dynamic class AdvancedProxy extends Proxy implements IEventDispatcher
	{
		private var _propertyMap:Object		=	new Object();
		private var _aliasMap:Object	=	new Object();
		private var _$super:*;
		private var _properties:Array	=	new Array();
		private var _needUpdateProperties:Boolean	=	false;
		private var _eventDispatcher:AdvancedEventDispatcher;
		
		public function AdvancedProxy()
		{
			super();
			_eventDispatcher	=	new AdvancedEventDispatcher(this);
		}
		
		/**
		 * 設定被代理的物件，會連事件也一併代理(僅轉發)。
		 */
		public function get $super():*
		{
			return _$super;
		}
		public function set $super(v:*):void
		{
			if(_$super == v)
				return;
			_$super	=	v;
			_eventDispatcher.eventSource	=	_$super;
		}
		
		/**
		 * 幫屬性及方法增加別名。
		 * 原先的方法名不會消失，別名僅是轉發。
		 * 若別名已存在，會蓋掉舊的別名。
		 * 別名會出現在for...in的迴圈之中。
		 * 刪除別名一樣使用delete的方法。
		 */
		public function $alias(name:String , alias:String):void
		{
			_aliasMap[alias]	=	name;
		}
		
		/**
		 * 傳入一個名字判斷是不是別名。
		 */
		public function $isAlias(name:*):Boolean
		{
			if(name is QName)
				name	=	name.localName;
			
			if(_aliasMap[name])
				return true;
			return false;
		}
		
		private function getRealName(name:*):*
		{
			if(name is QName)
				name	=	name.localName;
			
			if(_aliasMap.hasOwnProperty(name))
				return _aliasMap[name];
			
			return name;
		}
		
		override flash_proxy function setProperty(name:*, value:*):void
		{
			name	=	getRealName(name);
			
			if(this[name] == value)
				return;
			
			_needUpdateProperties	=	true;
			
			if(_hasProperty(name))
				_propertyMap[name]	=	value;
			else if(_$super && _$super.hasOwnProperty(name))
				_$super[name] = value;
			else
				_propertyMap[name]	=	value;
		}
		
		override flash_proxy function deleteProperty(name:*):Boolean
		{
			_needUpdateProperties	=	true;
			
			if($isAlias(name))
				return delete _aliasMap[name];
			
			return delete _propertyMap[name]; 
		}
		
		override flash_proxy function callProperty(name:*, ... rest):*
		{
			name	=	getRealName(name);
			
			if(_hasProperty(name))
				return _propertyMap[name].apply(this , rest);
			else if(_$super)
				return _$super[name].apply(this , rest);
		}
		
		override flash_proxy function getProperty(name:*):*
		{
			name	=	getRealName(name);
			
			if(_hasProperty(name))
				return _propertyMap[name];
			else if(_$super)
				return _$super[name];
		}
		
		override flash_proxy function nextName(index:int):String
		{
			updateProperties();
			return _properties[index-1].toString();
		}
		
		override flash_proxy function nextNameIndex(index:int):int
		{
			updateProperties()
			if (index < _properties.length)
				return index + 1;
			else
				return 0;
		}
		
		override flash_proxy function nextValue(index:int):*
		{
			updateProperties();
			if($isAlias(_properties[index-1]))
				return _aliasMap[_properties[index-1]];
			else
				return _propertyMap[_properties[index-1]];
		}
		
		override flash_proxy function hasProperty(name:*):Boolean
		{
			if(_hasProperty(name))
				return true;
			else if(_$super && _$super.hasOwnProperty(name))
				return true;
			else
				return false;
		}
		/**
		 * 尋找是否有這個屬性，含別名。
		 */
		private function _hasProperty(name:*):Boolean
		{
			if(name is QName)
				name	=	name.localName;
			if(name in _propertyMap)
				return true;
			if(name in _aliasMap)
				return true;
			return false;
		}
		/**
		 * 重整自已的屬性清單，含別名。
		 */
		private function updateProperties():void
		{
			if(_needUpdateProperties)
			{
				_properties	=	new Array();
				for (var p:String in _propertyMap)
					_properties.push(p);
				for (var a:String in _aliasMap)
					_properties.push(a);
				_needUpdateProperties	=	false;
			}
		}
		
		
		public final function addEventListener(type:String, listener:Function, useCapture:Boolean=false, priority:int=0, useWeakReference:Boolean=false):void
		{
			_eventDispatcher.addEventListener(type , listener , useCapture , priority , useWeakReference);
		}
		
		public final function removeEventListener(type:String, listener:Function, useCapture:Boolean=false):void
		{
			_eventDispatcher.addEventListener(type , listener , useCapture);
		}
		
		public final function dispatchEvent(event:Event):Boolean
		{
			return _eventDispatcher.dispatchEvent(event);
		}
		
		public final function hasEventListener(type:String):Boolean
		{
			return _eventDispatcher.hasEventListener(type);
		}
		
		public final function willTrigger(type:String):Boolean
		{
			return _eventDispatcher.willTrigger(type);
		}
		
		public final function getAllEventListenerType():Array
		{
			return _eventDispatcher.getAllEventListenerType();
		}
		
		public final function removeAllEventListener():void
		{
			_eventDispatcher.removeAllEventListener();
		}
		
		public final function removeEventListenerByType(type:String):Array
		{
			return _eventDispatcher.removeEventListenerByType(type);
		}
		
		public final function addEventListenerByArray(listeners:Array):void
		{
			_eventDispatcher.addEventListenerByArray(listeners);
		}
		
	}
}